#include <Windows.h>
#include "SkeletonAnalysis.h"

// class SkeletonAnalysis
SkeletonAnalysis::SkeletonAnalysis()
{
    resetRepeatCount();
}

SkeletonAnalysis::~SkeletonAnalysis()
{
}

void SkeletonAnalysis::resetRepeatContext(RepeatContext &rc)
{
    rc.minPoint     = -1;
    rc.maxPoint     = -1;
    rc.lastPoint1   = -1;
    rc.lastPoint2   = -1;
    rc.lowCount     = 0;
    rc.highCount    = 0;
}

void SkeletonAnalysis::resetRepeatCount()
{
    for(int s = 0; s < NUI_SKELETON_COUNT; s ++)
    {
        for(int p = 0; p < NUI_SKELETON_POSITION_COUNT; p ++)
        {
            for(int d = 0; d < 2; d ++)
            {
                resetRepeatContext(mRepeats[s][p][d]);
            }
        }
    }
}

void SkeletonAnalysis::initRepeatLimit()
{
    for(int p = 0; p < NUI_SKELETON_POSITION_COUNT; p ++)
    {
        for(int d = 0; d < 2; d ++)
        {
            mRepeatLimit[p][d] = 0.1f;
        }
    }
}

int SkeletonAnalysis::getRepeatCount(int s, int p, int d, bool isLow)
{
    if((s < 0) || (s >= NUI_SKELETON_COUNT))
    {
        return 0;
    }
    if((p < 0) || (p >= NUI_SKELETON_POSITION_COUNT))
    {
        return 0;
    }
    if((d < 0) || (d >= 2))
    {
        return 0;
    }
    return isLow ? mRepeats[s][p][d].lowCount : mRepeats[s][p][d].highCount;
}

static bool isValidPoint(const SkeletonPoint &v)
{
    return ((v.x >= 0) && (v.y >= 0));
}

static bool isValidPoint(int v1, int v2)
{
    return ((v1 >= 0) && (v2 >= 0));
}

static bool isMaxTurningPoint(int v2, int v1, int v)
{
    return ((v2 < v1) && (v < v1));
}

static bool isMinTurningPoint(int v2, int v1, int v)
{
    return ((v2 > v1) && (v > v1));
}

void SkeletonAnalysis::analysisRepeatCount(KinectLib *pKinect, int width, int height, const NUI_SKELETON_FRAME &frame)
{
    for(int s = 0; s < NUI_SKELETON_COUNT; s ++)
    {
        const NUI_SKELETON_TRACKING_STATE trackingState = frame.SkeletonData[s].eTrackingState;
        const NUI_SKELETON_DATA & skeletonData = frame.SkeletonData[s];
        if(trackingState != NUI_SKELETON_TRACKED)
        {
            continue ;
        }
        for(int p = 0; p < NUI_SKELETON_POSITION_COUNT; p ++)
        {
            if(skeletonData.eSkeletonPositionTrackingState[p] != NUI_SKELETON_POSITION_TRACKED)
            {
                continue ;
            }
            SkeletonPoint result = pKinect->convertSkeletonPosition(skeletonData.SkeletonPositions[p], width, height);
            if(!isValidPoint(result))
            {
                continue ;
            }
            for(int d = 0; d < 2; d ++)
            {
                const int xyPoint = (d == 0) ? result.x : result.y;
                const double wh = (d == 0) ? width : height;

                if(isValidPoint(mRepeats[s][p][d].lastPoint2, mRepeats[s][p][d].lastPoint1))
                {
                    if(isMaxTurningPoint(mRepeats[s][p][d].lastPoint2, mRepeats[s][p][d].lastPoint1, xyPoint))
                    {
                        mRepeats[s][p][d].maxPoint = mRepeats[s][p][d].lastPoint1;
                        if(isValidPoint(mRepeats[s][p][d].minPoint, mRepeats[s][p][d].maxPoint))
                        {
                            const int deltaX = (mRepeats[s][p][d].maxPoint - mRepeats[s][p][d].minPoint);
                            if(deltaX >= (mRepeatLimit[p][d]*wh))
                            {
                                mRepeats[s][p][d].highCount ++;
                            }
                            mRepeats[s][p][d].minPoint = -1;
                            mRepeats[s][p][d].maxPoint = -1;
                        }
                        mRepeats[s][p][d].minPoint = -1;
                    }
                    else if(isMinTurningPoint(mRepeats[s][p][d].lastPoint2, mRepeats[s][p][d].lastPoint1, xyPoint))
                    {
                        mRepeats[s][p][d].minPoint = mRepeats[s][p][d].lastPoint1;

                        if(isValidPoint(mRepeats[s][p][d].minPoint, mRepeats[s][p][d].maxPoint))
                        {
                            const int deltaX = (mRepeats[s][p][d].maxPoint - mRepeats[s][p][d].minPoint);
                            if(deltaX >= (mRepeatLimit[p][d]*wh))
                            {
                                mRepeats[s][p][d].lowCount ++;
                            }
                            mRepeats[s][p][d].minPoint = -1;
                            mRepeats[s][p][d].maxPoint = -1;
                        }
                        mRepeats[s][p][d].maxPoint = -1;
                    }
                }

                mRepeats[s][p][d].lastPoint2 = mRepeats[s][p][d].lastPoint1;
                mRepeats[s][p][d].lastPoint1 = xyPoint;
            }
        }
    }
}
